#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

from typing import Optional

from common.const import ExecuteResultEnum
from common.const import ReportDBLabel, DBLogLevel
from common.const import RpcToolInterface
from common.schemas.thrift_base_data_type import (ActionResult, SubJob, Copy,
                                                  RepositoryDataType)
from common.schemas.thrift_framework import SubJobStatus, SubJobDetails, LogDetail, ReportCopyInfo
from common.schemas.thrift_plugin import BackupJob
from saporacle.comm.common_util import rpctool_api, logger_watched
from saporacle.comm.constants import SAPOracleJobLabel
from saporacle.comm.logger import logger
from saporacle.comm.saporacle_exception import SAPOracleException
from saporacle.backup.data_backup_saporacle import DataBackupSAPOracle
from saporacle.comm.datamodels import BackintParam, DATACopyExtend
from saporacle.comm.repository import Repository
from saporacle.backup.saporacle_backup_service import SAPOracleBackupService


class SAPOracleDataBackupService(SAPOracleBackupService):

    def __init__(self, pid: str, saporacle: DataBackupSAPOracle, repository: Optional[Repository]) -> None:
        super().__init__(pid, saporacle, repository)

    def backup_prerequisite(self, backup_job: BackupJob) -> None:
        try:
            self._saporacle.all_brtools_exist()
            not self._saporacle.is_backint_installed() and self._saporacle.install_backint()
            self._saporacle.login()
        except SAPOracleException as e:
            logger.error(f"Backup prerequisite job failed. {e}")
            log_detail = LogDetail(logInfo=ReportDBLabel.PRE_REQUISIT_FAILED,
                                   logDetail=e.err_code, logDetailParam=e.parameters, logLevel=DBLogLevel.ERROR)
            job_details = SubJobDetails(taskId=backup_job.job_id, taskStatus=SubJobStatus.FAILED,
                                        logDetail=[log_detail])
            rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))
            return
        logger.info("Backup prerequisite job succeeded.")
        job_details = SubJobDetails(taskId=backup_job.job_id, taskStatus=SubJobStatus.COMPLETED)
        rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))

    def pause_job(self, backup_job: BackupJob, sub_job: SubJob) -> ActionResult:
        try:
            self._saporacle.terminate_brbackup()
        except SAPOracleException as e:
            logger.error(f"Pause backup failed. {e}")
            return ActionResult(code=ExecuteResultEnum.INTERNAL_ERROR, bodyErr=e.err_code, bodyErrParams=e.parameters)
        logger.info("Pause backup succeeded.")
        return ActionResult(code=ExecuteResultEnum.SUCCESS)

    """自定义备份子任务"""

    def prepare_sub_job(self, backup_job: BackupJob, sub_job: SubJob) -> None:
        # 下发的 channel_number 不是 int
        channel = min(int(backup_job.extend_info.get("channel_number")), self._saporacle.cpu_count()) \
            if backup_job.extend_info.get("channel_number") else 1
        logger.info(f"Data backup running. Backup channel_number is {backup_job.extend_info.get('channel_number')}, "
                    f"cpu_count is {self._saporacle.cpu_count()}, channel is {channel}.")
        backint_param = BackintParam(pid=self._pid, data_path=self._repository.data_paths[0], parallel=channel)
        self._saporacle.write_backint_param_file(backint_param.dict(by_alias=True))
        self._repository.make_data_dirs()
        self._saporacle.archive_current_redolog()
        logger.info("Prepare sub job succeeded.")
        log_detail = LogDetail(logInfo=ReportDBLabel.SUB_JOB_SUCCESS, logInfoParam=[sub_job.sub_job_id],
                               logLevel=DBLogLevel.INFO)
        job_details = SubJobDetails(taskId=backup_job.job_id, subTaskId=sub_job.sub_job_id,
                                    taskStatus=SubJobStatus.COMPLETED, logDetail=[log_detail])
        rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))

    @logger_watched(2)
    def backup_sub_job(self, backup_job: BackupJob, sub_job: SubJob) -> None:
        try:
            for trans, total in self._saporacle.run_brbackup():
                logger.info(f"Data backup running. {trans} MB / {total} MB")
                log_detail = LogDetail(logInfo=SAPOracleJobLabel.BACKUP_RUNNING_LABEL, logLevel=DBLogLevel.INFO,
                                       logInfoParam=[self._saporacle.host_name(), f"{total:.2f} MB", f"{trans:.2f} MB"])
                job_details = SubJobDetails(taskId=backup_job.job_id, subTaskId=sub_job.sub_job_id,
                                            taskStatus=SubJobStatus.RUNNING, logDetail=[log_detail])
                rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))
            self._report_copy_info(backup_job, sub_job)
        except SAPOracleException as e:
            logger.error("Data backup failed.")
            log_detail = LogDetail(logInfo=ReportDBLabel.SUB_JOB_FALIED, logInfoParam=[sub_job.sub_job_id],
                                   logDetail=e.err_code, logDetailParam=e.parameters, logLevel=DBLogLevel.ERROR)
            job_details = SubJobDetails(taskId=backup_job.job_id, subTaskId=sub_job.sub_job_id,
                                        taskStatus=SubJobStatus.FAILED, logDetail=[log_detail])
            rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))
            return
        logger.info("Data backup succeeded.")
        log_detail = LogDetail(logInfo=ReportDBLabel.SUB_JOB_SUCCESS, logInfoParam=[sub_job.sub_job_id],
                               logLevel=DBLogLevel.INFO)
        job_details = SubJobDetails(taskId=backup_job.job_id, subTaskId=sub_job.sub_job_id,
                                    dataSize=total * 1024, taskStatus=SubJobStatus.COMPLETED, logDetail=[log_detail])
        rpctool_api(self._pid, RpcToolInterface.REPORT_JOB_DETAIL, job_details.dict(by_alias=True))

    """private"""

    def _report_copy_info(self, backup_job: BackupJob, sub_job: SubJob) -> None:
        """
        :raise SAPOracleException:
        """
        repos = [repo for repo in backup_job.repositories if repo.repository_type == RepositoryDataType.DATA_REPOSITORY]
        [setattr(repo, "remote_path", f"{repo.remote_path}/{backup_job.copies[0].id}") for repo in repos]
        scn = self._repository.brbackup_scn()
        timestamp = self._saporacle.scns_to_timestamps([scn])[0]
        # UBC 框架问题 数据副本时间戳从 extendInfo 中读取
        extend = DATACopyExtend(backupScn=scn, backupTime=timestamp)
        _copy = Copy(repositories=repos, timestamp=timestamp, extendInfo=extend.dict(by_alias=True))
        copy_info = ReportCopyInfo(jobId=backup_job.job_id, copy=_copy)
        rpctool_api(self._pid, RpcToolInterface.REPORT_COPY_INFO, copy_info.dict(by_alias=True))

